<?php 
/**
 * This file is part of DomSql.
 * 
 * DomSql is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DomSql is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DomSql.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @link http://inditel.ee/
 * @copyright 2007-2009 Inditel Meedia OÜ
 * @author Oliver Leisalu <oliver@inditel.ee>
 */


/**
* {@code
// using foreach
foreach( $iterator as $key => $element ) {}
// using while
while( $element = $iterator->current() ) {

	$iterator->next();
}
@}
* @package DomSql
*/ 
class DomSqlIterator implements Iterator, Countable {
	
	/**
	 * Query that will be sent to database
	 *
	 * @var String
	 */
	protected $query;
	/**
	 * @var Resource
	 */
	protected $resource;
	/**
	 * @var String
	 */
	protected $connectionAlias;
	/**
	 * How results are handled
	 *
	 * @var Int MYSQL_ASSOC|MYSQL_NUM|MYSQL_BOTH
	 */
	protected $fetchType = MYSQL_ASSOC;
	/**
	 * Nr of rows in result. This variable used to cache the result.
	 *
	 * @var Int
	 */
	protected $foundRows;
	/**
	 * Variable that caches column index if it asked. See getColumnIndex() method.
	 *
	 * @var Array
	 */
	protected $columnIndex;
	/**
	 * This column value will be returned as key.
	 *
	 * @var String
	 */
	protected $indexColumnName = NULL;
	/**
	 * This column value will be returned as value. If not set, whole set will be returned (Usually expected)
	 * 
	 * @var String
	 */
	protected $valueColumnName = NULL;
	/**
	 * Iteration variable. Current item key. This can be number or string. It depends on property named "indexColumnName"
	 *
	 * @var Mixed
	 */
	protected $currentKey;
	/**
	 * Iteration variable. Current value.
	 *
	 * @var Mixed
	 */
	protected $current;
	
	/**
	 * @param String $query
	 * @param String $alias
	 * @return DomSqlIterator
	 */
	public static function c( $query, $alias = DomSql::DEFAULT_ALIAS ) {
		return new DomSqlIterator( $query, $alias  );
	}
	/**
	 * @param String $query
	 * @param String $alias
	 */
	public function __construct( $query, $alias = DomSql::DEFAULT_ALIAS ) {
		$this->query = $query;
		$this->connectionAlias = $alias;
		$this->execute();
	}
	/**
	 * Destructor. Frees memory.
	 */
	public function __destruct() {
		if( $this->resource )
			mysql_free_result($this->resource);
	}
	/**
	 * Allows acccess to all properties used by iterator
	 * 
	 * @param String $var
	 * @return Mixed
	 */
	public function __get( $var ) {
		return $this->$var;
	}
	/**
	* Returns total nr of found rows. Select query must be commited with SQL_CALC_FOUND_ROWS flag. (SELECT SQL_CALC_FOUND_ROWS * FROM table WHERE 1=1)
	* If SQL_CALC_FOUND_ROWS is not added then this method works like count()
	* @return Int
	*/
	public function foundRows() {
		if( $this->foundRows === NULL )
			$this->foundRows = (int)DomSql::selectOneQuery( 'SELECT FOUND_ROWS() as n','n' );
		return (int)$this->foundRows;
	}
	/**
	 * This column value is returned as key. (Key's don't have to be unique, you will still get all results!)
	 * @var String
	 * @return DomSqlIterator
	 */
	public function setIndexColumnName( $indexColumnName ) {
		$this->indexColumnName = $indexColumnName;
		return $this;
	}
	/**
	 * See valueColumnName
	 * @var String
	 * @return DomSqlIterator
	 */
	public function setValueColumnName( $valueColumnName ) {
		$this->valueColumnName = $valueColumnName;
		return $this;
	}
	/**
	 * Set fetch type. Possible values: MYSQL_NUM, MYSQL_ASSOC, MYSQL_BOTH
	 *
	 * @param String $const
	 * @return DomSqlIterator
	 */
	public function setFetchType( $const ) {
		$this->fetchType = $const;
		return $this;
	}
	/**
	 * Return list of column names and table aliases. This is useful for queryies that return result as a numberical list.
	 * Helps to determine from which table each value came.
	 * 
	 * NB! Works only if fetchType is  MYSQL_NUM
	 *
	 * Result format: array[(int)offset] = array('table' => String, 'column' => String)
	 * 
	 * @return Array
	 */
	public function getColumnIndex() {
		
		if( $this->columnIndex !== NULL )
			return $this->columnIndex;
		
		$this->columnIndex = array();
		for( $i=0; $i<mysql_num_fields($this->resource); $i++ )
			$this->columnIndex[$i] = array('table' => mysql_field_table($this->resource, $i),'column' => mysql_field_name($this->resource, $i) );
		
		return $this->columnIndex;
	}
	/**
	* Parses current
	* Use this method if you want to make changes to current item every time it is loaded.
	* @return void
	*/
	protected function parseCurrent() {
		return;
	}
	/**
	* Returns iterator elements as array.
	* Pointer is set to end.
	* @param Array $baseArray Array to which elements will be added
	* @return Array
	*/
	public function toArray( array $baseArray = array() ) {
		//$this->rewind();
		foreach( $this as $k => $item )
			$baseArray[$k] = $item;
		return $baseArray;
	}
	/**
	 * Executes query.
	 */
	protected function execute() {
		$this->resource = DomSql::query( $this->query, $this->connectionAlias );
	}

	//----------------------------------------------------
	// Iterator 
	//----------------------------------------------------
	
	public function rewind() {
		if( $this->currentKey !== NULL )
			throw new BadMethodCallException('DomSqlIterator cannot be rewinded! (You can\'t traverse DomSql iterators twice!)');
			
		$this->currentKey = -1;
		if( $this->resource )
			$this->next();
		return $this;
	}
	
	public function current() {	
		return $this->current;
	}
	
	public function key() {
		return $this->currentKey;
	}
	
	public function next() {
		
		$this->current = mysql_fetch_array( $this->resource, $this->fetchType );
		if( $this->current )
			$this->parseCurrent();

		// key column
		if( $this->indexColumnName !== NULL )
			if( is_object($this->current) === true ) {
				$this->currentKey = $this->current->{$this->indexColumnName};
				// this is fix for DomArObjects. When you want DomAr object as key, you probably mean its ID.
				if( $this->currentKey instanceof DomArObject )
					$this->currentKey = $this->currentKey->id;
			} else
				$this->currentKey = $this->current[$this->indexColumnName];
		else
			$this->currentKey++;
		
		// value column
		if( $this->valueColumnName !== NULL ) {
			if( is_object($this->current) === true )
				$this->current = $this->current->{$this->valueColumnName};
			else
				$this->current = $this->current[$this->valueColumnName];
		}
			
		return $this->current;
	}
	
	public function valid() {
		return $this->current != false;
	}
	
	//----------------------------------------------------
	// Countable 
	//----------------------------------------------------
	
	public function count() {
		if( $this->resource ) { 
			return mysql_num_rows($this->resource);
		} else {
			return 0;
		}
	}
	
}

/**
* Iterator that returns id-s as keys and single data field as value.
* @package DomAr
* @subpackage Iterator
*/	
class DomSqlKeyListIterator extends DomSqlIterator {
	
	protected $key;
	
	protected $value;
	
	public static function c( $query, $key, $value = NULL, $alias = DomSql::DEFAULT_ALIAS ) {
		return new DomSqlIterator( $query, $key, $value, $alias  );
	}
	
	public function __construct( $query, $key, $value = NULL,  $alias = DomSql::DEFAULT_ALIAS ) {
		$this->query = $query;
		$this->connectionAlias = $alias;
		$this->key = $key;
		$this->value = $value;
		$this->execute();
	}
	
	protected function parseCurrent() {
		$this->currentKey = $this->current[$this->key];
		if( $this->value !== NULL)
			$this->current = $this->current[$this->value];
	}

	/**
	* Returns iterator elements as array.
	* Pointer is set to end.
	* @param Array $baseArray Array to which elements will be added
	* @return Array
	*/
	public function toArray( array $baseArray = array() ) {
		//$this->rewind();
		foreach( $this as $key => $item )
			$baseArray[$key] = $item;
		return $baseArray;
	}
	
	public function next() {
		$this->current = mysql_fetch_assoc( $this->resource );
		$this->currentKey = false;
		if( $this->current )
			$this->parseCurrent();

		return $this->current;
	}
	
	public function valid() {
		return $this->currentKey != false;
	}
}
?>